package com.wmada.toolkit.common.secure;

import org.apache.commons.codec.binary.Base64;

/**
 * @author will.ma
 * @date 2017/3/11
 */
public class EncodeUtils {

    private static final char[] ZERO_SEQUENCE = "0000000000000000000000000000000000000000000000000000000000000000".toCharArray();

    private static final char[] HEX_DIGEST            = "0123456789abcdef".toCharArray();
    private static final char[] HEX_DIGEST_UPPER_CASE = "0123456789ABCDEF".toCharArray();
    private static final String HEX_POSITION          = "0123456789abcdef";

    private static final Base64 BASE_64 = new Base64();

    private static final String BASE62INDEX = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final int    BASE62      = 62;
    private static final int    BASE36      = 36;

    public static String base64Encode(byte[] data) {
        return BASE_64.encodeToString(data);
    }

    public static byte[] base64Decode(String message) {
        return BASE_64.decode(message);
    }

    public static String bytes2Hex(byte[] data) {
        if (data != null) {
            char[] c = new char[data.length * 2];
            for (int i = 0; i < data.length; i++) {
                byte b = data[i];
                c[(2 * i)] = HEX_DIGEST[((b & 0xF0) >> 4)];
                c[(2 * i + 1)] = HEX_DIGEST[(b & 0xF)];
            }
            return new String(c);
        } else {
            return null;
        }
    }

    public static byte[] hex2Bytes(String message) {
        if (message != null && message.length() % 2 == 0) {
            message = message.toLowerCase();
            byte[] c = new byte[message.length() / 2];
            for (int i = 0; i < c.length; i++) {
                char c1   = message.charAt(2 * i);
                char c2   = message.charAt(2 * i + 1);
                int  pos1 = HEX_POSITION.indexOf(c1);
                int  pos2 = HEX_POSITION.indexOf(c2);
                c[i] = (byte) ((pos1 & 0x0F) << 4 | (pos2 & 0x0F));
            }
            return c;
        } else {
            return null;
        }
    }

    public static String bytes2HexUpperCase(byte[] data) {
        char[] c = new char[data.length * 2];
        for (int i = 0; i < data.length; i++) {
            byte b = data[i];
            c[(2 * i)] = HEX_DIGEST_UPPER_CASE[((b & 0xF0) >> 4)];
            c[(2 * i + 1)] = HEX_DIGEST_UPPER_CASE[(b & 0xF)];
        }
        return new String(c);
    }

    public static String zeroFill(int n, int len) {
        if (n < 0 || len < 0) {
            return "";
        }
        return zeroFill(Integer.toString(n), len);
    }

    public static String zeroFill(long n, int len) {
        if (n < 0 || len < 0) {
            return "";
        }
        return zeroFill(Long.toString(n), len);
    }

    private static String zeroFill(String num, int len) {
        int zeroLength = len - num.length();
        if (zeroLength == 0) {
            return num;
        } else if (zeroLength > 0) {
            return String.valueOf(ZERO_SEQUENCE, 0, zeroLength) + num;
        } else {
            return num.substring(0, len);
        }
    }

    public static byte[] long2Bytes(long n) {
        byte[] bytes = new byte[8];
        bytes[0] = (byte) (n & 0x00FF);
        bytes[1] = (byte) ((n >> 8) & 0x00FF);
        bytes[2] = (byte) ((n >> 16) & 0x00FF);
        bytes[3] = (byte) ((n >> 24) & 0x00FF);
        bytes[4] = (byte) ((n >> 32) & 0x00FF);
        bytes[5] = (byte) ((n >> 40) & 0x00FF);
        bytes[6] = (byte) ((n >> 48) & 0x00FF);
        bytes[7] = (byte) ((n >> 56) & 0x00FF);
        return bytes;
    }

    public static long bytes2Long(byte[] bytes) {
        if (bytes == null || bytes.length != 8) {
            return 0L;
        }
        return (((long) bytes[7] & 0x00FF) << 56) | (((long) bytes[6] & 0x00FF) << 48) | (((long) bytes[5] & 0x00FF) << 40) | (((long) bytes[4] & 0x00FF) << 32) | (((long) bytes[3] & 0x00FF) << 24) | (((long) bytes[2] & 0x00FF) << 16) | (((long) bytes[1] & 0x00FF) << 8) | ((long) bytes[0] & 0x00FF);
    }

    public static byte[] int2Bytes(int n) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) (n & 0x00FF);
        bytes[1] = (byte) ((n >> 8) & 0x00FF);
        bytes[2] = (byte) ((n >> 16) & 0x00FF);
        bytes[3] = (byte) ((n >> 24) & 0x00FF);
        return bytes;
    }

    public static int bytes2Int(byte[] bytes) {
        if (bytes == null || bytes.length != 4) {
            return 0;
        }
        return (((int) bytes[3] & 0x00FF) << 24) | (((int) bytes[2] & 0x00FF) << 16) | (((int) bytes[1] & 0x00FF) << 8) | ((int) bytes[0] & 0x00FF);
    }

    /**
     * Encode base 62 string.
     *
     * @param n the n
     * @return the string
     */
    public static String encodeBase62(long n) {
        return encodeOfBase(n, BASE62);
    }

    /**
     * Encode base 36 string.
     *
     * @param n the n
     * @return the string
     */
    public static String encodeBase36(long n) {
        return encodeOfBase(n, BASE36);
    }

    /**
     * Encode of base string.
     *
     * @param n    the n
     * @param base the base
     * @return the string
     */
    public static String encodeOfBase(long n, int base) {
        StringBuilder ret    = new StringBuilder();
        int           a;
        double        number = n;
        for (int t = (int) Math.floor(Math.log10(number) / Math.log10(BASE36)); t >= 0; t--) {
            a = (int) Math.floor(number / Math.pow(BASE36, t));
            ret.append(BASE62INDEX.charAt(a));
            number = number - a * Math.pow(BASE36, t);
        }
        return ret.toString();
    }
}
